package com.mlnx.data.dao.iml;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mlnx.common.utils.MyLog;
import com.mlnx.data.dao.EcgStore;
import com.mlnx.data.entity.EcgInfo;
import com.mlnx.data.pojo.vo.EcgHeartVo;
import com.mlnx.data.utils.ZipUtil;
import com.mlnx.service.base.properties.MutilDeviceProperties;
import com.mlnx.service.common.utils.TimeRecordUtils;
import org.bson.types.Binary;
import org.shan.spring.mongodb.MongodbUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * Created by amanda.shan on 2017/5/10.
 */
@Repository("ecgMongoDb")
public class EcgMongoDb implements EcgStore {

    private MyLog log = MyLog.getLog(EcgMongoDb.class);

    private TimeRecordUtils timeRecordUtils = new TimeRecordUtils();

    private Long saveTimeBySecond = 0L;

    @Autowired
    private MutilDeviceProperties mutilDeviceProperties;

    @PostConstruct
    @Override
    public void init() {

        log.info("开始初始化ecg mongodb存储");

        try {
            // 设置ecg索引
            Map<String, Sort.Direction> map = new HashMap<>();
            map.put("patientId", Sort.Direction.ASC);
            map.put("startTime", Sort.Direction.ASC);
            MongodbUtils.setIndex(mutilDeviceProperties.getStore().getEcgCollectionName(), map, true);
        } catch (Exception e) {
            log.error(e, "设置心电索引失败，失败已经捕捉不影响使用");
        }

        // 设置ecg的patintID索引
//        map.remove("startTime");
//        MongodbUtils.setIndex(mutilDeviceProperties.ecgCollectionName, map, true);

        log.info("初始化ecg mongodb存储结束");
    }

    @Override
    public Long getSaveTimeBySecond() {
        return saveTimeBySecond;
    }

    @Override
    public boolean save(EcgInfo ecg) {

        long startTime = System.currentTimeMillis();

        zipEcg(ecg);
        saveEcg(ecg);

        Long time = timeRecordUtils.addTime(System.currentTimeMillis() - startTime);
        if (time != null) {
            saveTimeBySecond = time;
            log.info("每秒钟存储耗时时间:" + saveTimeBySecond);
        }

        return true;
    }

    @Override
    public boolean save(List<EcgInfo> ecgs) {

        boolean s = true;
        long startTime = System.currentTimeMillis();

//        byte[] zips = ZipUtil.zip(ecgs.get(0).getFilterData());
//        log.info(String.format("原始数据:%d  压缩数据:%d", ecgs.get(0).getFilterData().length, zips.length));

        List<EcgInfo> failEcgs = new ArrayList<>();
        for (int i = 0; i < ecgs.size(); i++) {
            try {
                save(ecgs.get(i));
            } catch (RuntimeException e) {
                log.error(e, "保存心电数据失败");
                failEcgs.add(ecgs.get(i));
            }
        }
        if (failEcgs.size() > 0) {
            log.error(String.format("EcgMongoDb saveEcg 总共保存：%d, 失败: %d", ecgs.size(), failEcgs.size()));
            s = false;
        }

        Long time = timeRecordUtils.addTime(System.currentTimeMillis() - startTime);
        if (time != null) {
            saveTimeBySecond = time;
            log.info("每秒钟存储耗时时间:" + saveTimeBySecond);
        }
        return s;
    }

    @Override
    public List<EcgHeartVo> getEcgHeartVos(long startTime, long endTime, int patientId, Integer limit) {

        Criteria criteria = Criteria.where("patientId").is(patientId);
        criteria.and("startTime").lte(endTime).gte(startTime);
        Query query = Query.query(criteria);
        query.limit(limit).with(new Sort(Sort.Direction.ASC, "startTime"));
        query.fields().include("startTime").include("heartRate").exclude("_id");

        List<EcgHeartVo> ecgHeartVos = MongodbUtils.find(EcgHeartVo.class, query,
                mutilDeviceProperties.getStore().getEcgCollectionName());

        return ecgHeartVos;
    }

    @Override
    public void delete(Integer patientId) {
        MongodbUtils.removeByKey("patientId", patientId, mutilDeviceProperties.getStore().getEcgCollectionName());
    }

    @Override
    public long count(Integer patientId) {
        Criteria criteria = Criteria.where("patientId").is(patientId);
        Query query = Query.query(criteria);
        return MongodbUtils.count(query, mutilDeviceProperties.getStore().getEcgCollectionName());
    }

    public void saveEcg(EcgInfo ecg) {
        MongodbUtils.save(ecg, mutilDeviceProperties.getStore().getEcgCollectionName());
    }

    @Override
    public List<Map> getEcg(long startTime, long endTime, int patientId, Integer limit) {
        return getEcgData(startTime, endTime, patientId, "data", limit);
    }

    @Override
    public List<Map> getEncryEcg(long startTime, long endTime, int patientId, Integer limit) {
        return getEcgData(startTime, endTime, patientId, "encryData", limit);
    }

    @Override
    public List<Map> getFilterEcg(long startTime, long endTime, int patientId, Integer limit) {
        return getEcgData(startTime, endTime, patientId, "filterData", limit);
    }

    @Override
    public List<Map> getFilterEcg(Integer patientId, Long startTime, Integer limit) {
        return getEcgData(startTime, limit, patientId, "filterData");
    }

    @Override
    public List<Map> getEcg(Integer patientId, Long startTime, Integer limit) {
        return getEcgData(startTime, limit, patientId, "data");
    }

    @Override
    public List<Map> getEncryEcg(Integer patientId, Long startTime, Integer limit) {
        return getEcgData(startTime, limit, patientId, "encryData");
    }

    @Override
    public List<Map> getLastEcgTime(Integer patientId, Long startTime, Integer limit) {

        Criteria criteria = Criteria.where("patientId").is(patientId);
        criteria.and("startTime").lte(startTime);
        Query query = Query.query(criteria);
        query.limit(limit).with(new Sort(Sort.Direction.DESC, "startTime"));
        query.fields().include("startTime").include("patientId").include("deviceId").include("deviceType").exclude
                ("_id");

        return MongodbUtils.find(Map.class, query, mutilDeviceProperties.getStore().getEcgCollectionName());
    }


    public List<Map> getEcgData(long startTime, long endTime, int patientId, String ecgDataKey, int limit) {
        Criteria criteria = Criteria.where("patientId").is(patientId);
        criteria.and("startTime").lte(endTime).gte(startTime);
        Query query = Query.query(criteria);
        query.limit(limit).with(new Sort(Sort.Direction.ASC, "startTime"));
        query.fields().include("startTime").include("patientId").include("deviceId").include("deviceType")
                .include("heartRate").include("pei").include(ecgDataKey).exclude("_id");

        List<Map> maps = MongodbUtils.find(Map.class, query, mutilDeviceProperties.getStore().getEcgCollectionName());

        if (maps != null) {

            for (int i = 0; i < maps.size(); i++) {
                Map map = maps.get(i);

                if (map.containsKey(ecgDataKey)) {
                    org.bson.types.Binary binary = (Binary) map.get(ecgDataKey);
                    map.put("data", binary.getData());
                    map.remove(ecgDataKey);
                    if (!"data".equals(ecgDataKey)) {
                        map.remove(ecgDataKey);
                    }
                }
            }
        }
        return maps;
    }

    private List<Map> getEcgData(long startTime, int limit, int patientId, String ecgDataKey) {
        Criteria criteria = Criteria.where("patientId").is(patientId);
        criteria.and("startTime").lte(System.currentTimeMillis()).gt(startTime);
        Query query = Query.query(criteria);
        query.limit(limit).with(new Sort(Sort.Direction.ASC, "startTime"));
        query.fields().include("startTime").include("patientId").include("deviceId").include("deviceType")
                .include("heartRate").include("pei").include(ecgDataKey).exclude("_id");

        List<Map> maps = MongodbUtils.find(Map.class, query, mutilDeviceProperties.getStore().getEcgCollectionName());

        if (maps != null) {

            for (int i = 0; i < maps.size(); i++) {
                Map map = maps.get(i);

                if (map.containsKey(ecgDataKey)) {
                    org.bson.types.Binary binary = (Binary) map.get(ecgDataKey);
                    map.put("data", binary.getData());
                    if (!"data".equals(ecgDataKey)) {
                        map.remove(ecgDataKey);
                    }
                }
            }
        }
        return maps;
    }

    private List<Map> getEcgData(long startTime, int limit, String ecgDataKey) {

        Criteria criteria = Criteria.where("patientId").lte(System.currentTimeMillis()).gte(startTime);
        Query query = Query.query(criteria);
        query.limit(limit).with(new Sort(Sort.Direction.ASC, "startTime"));
        query.fields().include("startTime").include("patientId").include("deviceId").include("deviceType")
                .include("heartRate").include("pei").include(ecgDataKey).exclude("_id");

        List<Map> maps = MongodbUtils.find(Map.class, query, mutilDeviceProperties.getStore().getEcgCollectionName());

        if (maps != null) {

            for (int i = 0; i < maps.size(); i++) {
                Map map = maps.get(i);

                if (!ecgDataKey.equals("data") && map.containsKey("ecgDataKey")) {
                    map.put("data", map.get(ecgDataKey));
                    map.remove(ecgDataKey);
                }
            }
        }
        return maps;

    }


    /**
     * 压缩心电数据
     *
     * @param ecg
     */
    private void zipEcg(EcgInfo ecg) {

        if (ecg.getData() != null) {
            if (mutilDeviceProperties.getStore().isStoreSrcEcg()) {
                ecg.setData(ZipUtil.zip(ecg.getData()));
            } else {
                ecg.setData(null);
            }
        }

        if (ecg.getEncryData() != null) {
            ecg.setEncryData(ZipUtil.zip(ecg.getEncryData()));
        }

        if (ecg.getFilterData() != null) {
            ecg.setFilterData(ZipUtil.zip(ecg.getFilterData()));
        }
    }

    /**
     * 解压缩压缩心电数据
     */
    private JSONObject unZipEcg(JSONObject jsonObject) {

        EcgInfo ecg = JSON.parseObject(jsonObject.toString(), EcgInfo.class);

        if (ecg.getData() != null) {
            ecg.setData(ZipUtil.unGZip(ecg.getData()));
        }

        return null;
    }


    private static List<EcgInfo> getSimEcgs() {

        List<EcgInfo> ecgs = new ArrayList<EcgInfo>();

        for (int patientId = 1; patientId < 2; patientId++) {
            for (int i = 0; i < 10; i++) {
                EcgInfo ecg = new EcgInfo();

                ecg.setPatientId(patientId);
                ecg.setDeviceId("hek07bb123456");
                ecg.setStartTime(System.currentTimeMillis() - 1000 * i);
//                ecg.setStartTime(100000L);
                ecg.setSamplingRate(300);
                ecg.setAmplification(5);
                ecg.setHeartRate(90);
                ecg.setPose(5);


                byte[] bytes = new byte[200];
                for (int j = 0; j < bytes.length; j++) {
                    bytes[j] = (byte) j;
                }
//                ByteBuffer byteBuffer = ByteBuffer.allocate(2000);
//                byteBuffer.put(bytes);
//                byteBuffer.flip();
                ecg.setData(bytes);

                ecgs.add(ecg);
            }
        }

        return ecgs;
    }

    @Override
    public void saveEcgReportData(String reportNumeration, byte[] bytes) {
    }

    @Override
    public byte[] getEcgReportData(String reportNumeration) {
        return null;
    }
}
